package homework1

import (
	"fmt"
	"regexp"
	"strings"
)

type router struct {
	// trees 是按照 HTTP 方法来组织的
	// 如 GET => *node
	trees map[string]*node
}

func newRouter() router {
	return router{
		trees: map[string]*node{},
	}
}

type nodeType int

const (
	// 静态路由
	nodeTypeStatic = iota
	// 正则路由
	nodeTypeReg
	// 路径参数路由
	nodeTypeParam
	// 通配符路由
	nodeTypeAny
)

// node 代表路由树的节点
// 路由树的匹配顺序是：
// 1. 静态完全匹配
// 2. 正则匹配，形式 :param_name(reg_expr)
// 3. 路径参数匹配：形式 :param_name
// 4. 通配符匹配：*
// 这是不回溯匹配
type node struct {
	typ nodeType

	path string
	// children 子节点
	// 子节点的 path => node
	children map[string]*node
	// handler 命中路由之后执行的逻辑
	handler HandleFunc

	// 通配符 * 表达的节点，任意匹配
	starChild *node

	paramChild *node
	// 正则路由和参数路由都会使用这个字段
	paramName string

	// 正则表达式
	regChild *node
	regExpr  *regexp.Regexp
}

// addRoute 注册路由。
// method 是 HTTP 方法
// path 必须以 / 开始并且结尾不能有 /，中间也不允许有连续的 /
func (r *router) addRoute(method string, path string, handler HandleFunc) {
	if path == "" {
		panic("web：路由必须以 / 开头")
	}
	if path[0] != '/' {
		panic("web：路由必须以 / 开头")
	}
	if path[0] == '/' && len(path) > 1 && path[len(path)-1] == '/' {
		panic("web: 路由不能以 / 结尾")
	}
	root, ok := r.trees[method]
	if !ok {
		//
		root = &node{path: "/"}
		r.trees[method] = root
	}
	if path == "/" {
		if root.handler != nil {
			panic("web: 路由冲突[/]")
		}
		root.handler = handler
		return
	}
	segs := strings.Split(path[1:], "/")
	for _, s := range segs {
		if s == "" {
			panic(fmt.Sprintf("web: 非法路由。不允许使用 //a/b, /a//b 之类的路由, [%s]", path))
		}
		root = root.childOrCreate(s)
	}
	if root.handler != nil {
		panic(fmt.Sprintf("web: 路由冲突[%s]", path))
	}
	root.handler = handler
}
func (r *router) findRoute(method string, path string) (*matchInfo, bool) {
	root, ok := r.trees[method]
	if !ok {
		return nil, false
	}
	if path == "/" {
		return &matchInfo{n: root}, true
	}
	segs := strings.Split(strings.Trim(path, "/"), "/")
	mi := &matchInfo{}
	for _, s := range segs {
		child, matchParam, ok := root.childOf(s)

		if matchParam {
			mi.addValue(child.path[1:], s)
			// 正则路由查找
			regChild, ok := root.childOfReg(s)
			if ok {
				root = regChild
				break
			}
		}
		if !ok {
			return nil, false
		}

		// 通配符匹配多个路径
		if root.starChild != nil && root.starChild.typ == nodeTypeAny {
			root = child
			break
		}
		root = child
	}
	mi.n = root
	return mi, true
}

// childOrCreate 查找子节点，如果子节点不存在就创建一个
// 并且将子节点放回去了 children 中
// childOrCreate 查找子节点，
// 首先会判断 path 是不是通配符路径
// 其次判断 path 是不是参数路径，即以 : 开头的路径
// 最后会从 children 里面查找，
// 如果没有找到，那么会创建一个新的节点，并且保存在 node 里面
func (n *node) childOrCreate(path string) *node {
	if path[0] == ':' {
		if n.starChild != nil {
			panic("web ： 通用参数不允许同时注册路径参数和通配符匹配，已有通配符参数匹配")
		}
		if n.regChild != nil {
			panic("web ： 通用参数不允许同时注册路径参数和正则匹配，已有正则匹配")
		}
		// 正则表达式匹配，正则和路径参数二选一
		regChildNode, ok := n.childOrCreateReg(path)
		if ok {
			n.regChild = regChildNode
			return n.regChild
		}
		if n.paramChild == nil {
			n.paramChild = &node{path: path, typ: nodeTypeParam}
		}
		return n.paramChild
	}
	if path == "*" {
		if n.paramChild != nil {
			panic("web ： 通用参数不允许同时注册路径参数和通配符匹配，已有路径参数匹配")
		}
		if n.regChild != nil {
			panic("web ： 用参数不允许同时注册路径参数和通配符匹配，已有正则匹配")
		}
		if n.starChild == nil {
			n.starChild = &node{path: "*", typ: nodeTypeAny}
		}
		return n.starChild
	}
	if n.children == nil {
		n.children = make(map[string]*node)
	}
	child, ok := n.children[path]
	if !ok {
		child = &node{path: path, typ: nodeTypeStatic}
		n.children[path] = child
	}
	return child
}

func (n *node) childOrCreateReg(path string) (*node, bool) {
	if path[0] != ':' {
		return nil, false
	}
	if strings.Contains(path, "(") && strings.Contains(path, ")") {
		return &node{
			path: path,
			typ:  nodeTypeReg,
		}, true
	}
	return nil, false
}

// childof 优先考虑静态资源，匹配不上再考虑通配符资源
// child 返回子节点
// 第一个返回值 *node 是命中的节点
// 第二个返回值 bool 代表是否是命中参数路由
// 第三个返回值 bool 代表是否命中通配符
// 第四个返回值 bool 代表是否命中通配符且通配符后续没子节点
func (n *node) childOf(path string) (*node, bool, bool) {
	if n.children == nil {
		if n.paramChild != nil {
			return n.paramChild, true, true
		}
		if n.regChild != nil {
			return n.regChild, true, true
		}
		return n.starChild, false, n.starChild != nil
	}
	child, ok := n.children[path]
	if !ok {
		if n.paramChild != nil {
			return n.paramChild, true, true
		}
		return n.starChild, false, n.starChild != nil
	}

	return child, false, ok
}

func (n *node) childOfReg(path string) (*node, bool) {
	if n.regChild == nil {
		return nil, false
	}
	ok, err := regexp.Match(n.regChild.path, []byte(path))
	if err != nil {
		return nil, false
	}
	if ok {
		return &node{path: path, typ: nodeTypeReg}, true
	}
	return nil, false
}

type matchInfo struct {
	n          *node
	pathParams map[string]string
}

func (mi *matchInfo) addValue(key string, value string) {
	if mi.pathParams == nil {
		mi.pathParams = map[string]string{key: value}
	}
	mi.pathParams[key] = value
}
